import {
  objectToString,
  objectToStringFormat,
  circleSingleLinkedList
} from "../../shared";
import { AbstractList, IListConstructor } from "./list-type";
import { ListNode } from "./list-node";
/**
 * 循环单链表构造器
 */
export interface CircleSingleLinkedIListConstructor<T>
  extends IListConstructor<T> {
  new (datas: T[]): CircleSingleLinkedList<T>;
  readonly [Symbol.species]: CircleSingleLinkedIListConstructor<T>;
  readonly prototype: CircleSingleLinkedList<T>;
  isCircleSingleLinkedList<T>(obj: unknown): obj is CircleSingleLinkedList<T>;
  of<T>(...datas: T[]): CircleSingleLinkedList<T>;
  from<T>(datas: T[]): CircleSingleLinkedList<T>;
}
/**
 * 判断是否是循环单链表
 * @param obj 任意对象
 */
export function isCircleSingleLinkedList<T>(
  obj: unknown
): obj is CircleSingleLinkedList<T> {
  return obj instanceof CircleSingleLinkedList;
}
/**
 * 循环单链表
 */
export class CircleSingleLinkedList<T> extends AbstractList<T> {
  static [Symbol.hasInstance](instance: unknown): boolean {
    return (
      objectToString(instance) === objectToStringFormat(circleSingleLinkedList)
    );
  }
  static from<T>(datas: T[]): CircleSingleLinkedList<T> {
    return new CircleSingleLinkedList(datas);
  }
  static of<T>(...datas: T[]): CircleSingleLinkedList<T> {
    return new CircleSingleLinkedList(datas);
  }
  static isCircleSingleLinkedList<T>(
    obj: unknown
  ): obj is CircleSingleLinkedList<T> {
    return isCircleSingleLinkedList(obj);
  }
  static get [Symbol.species](): CircleSingleLinkedIListConstructor<any> {
    return CircleSingleLinkedList;
  }
  [Symbol.toStringTag] = circleSingleLinkedList;
  constructor(datas: T[] = []) {
    super();
    for (const el of datas) this.add(el);
  }
  add(element: T, index: number = this._size): void {
    this.checkAddRange(index);
    if (this._firstNode === null) {
      const node = new ListNode<T>(element);
      node.next = node;
      this._firstNode = node;
    } else if (index === 0) {
      const prevNode = this._firstNode;
      const nowNode = new ListNode<T>(element, null, prevNode);
      prevNode.next = nowNode;
      this._firstNode = nowNode;
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k === index - 1) {
          v.next = new ListNode<T>(element, null, v.next);
          break;
        }
    }
    this._size++;
  }
  delete(index: number): T {
    this.checkRange(index);
    let el: T = (this._firstNode as ListNode<T>).element;
    if (this._size === 1) {
      this.clear();
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k === index - 1) {
          const next = v.next as ListNode<T>;
          el = next.element;
          v.next = next.next;
          break;
        }
      this._size--;
    }
    return el;
  }
}
